/*
 * Copyright (c) 2021-2023, Arm Limited and Contributors. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

#include "Driver_USART.h"

#include <stdarg.h>
#include <stdio.h>

static ARM_DRIVER_USART *serial_obj = NULL;

extern ARM_DRIVER_USART *get_example_serial();

int iotsdk_serial_setup()
{
    if (serial_obj != NULL) {
        return 0;
    }

    serial_obj = get_example_serial();
    if (serial_obj == NULL) {
        return -1;
    }

    if ((serial_obj->Initialize(NULL) != ARM_DRIVER_OK) || (serial_obj->PowerControl(ARM_POWER_FULL) != ARM_DRIVER_OK)
        || (serial_obj->Control(ARM_USART_MODE_ASYNCHRONOUS, 115200) != ARM_DRIVER_OK)) {
        return -1;
    }
    /* Some drivers have TX and RX enabled by default and lacks option to enable/disable them. */
    int ret = serial_obj->Control(ARM_USART_CONTROL_TX, 1);
    if (ret != ARM_DRIVER_OK && ret != ARM_DRIVER_ERROR_UNSUPPORTED) {
        return -1;
    }
    ret = serial_obj->Control(ARM_USART_CONTROL_RX, 1);
    if (ret != ARM_DRIVER_OK && ret != ARM_DRIVER_ERROR_UNSUPPORTED) {
        return -1;
    }

    return 0;
}

int iotsdk_serial_out(const char *str, size_t len)
{
    if (str == NULL || len == 0) {
        return 0;
    }

    if (serial_obj->Send(str, len) != ARM_DRIVER_OK) {
        return 0;
    }

    /* Send is non-blocking. Add busy wait to make sure it has completed. */
    while (serial_obj->GetStatus().tx_busy)
        ;

    /* Save the number of characters sent before potentially adding a carriage return, as
     * the added carriage return is not counted by printf.
     */
    int tx_count = serial_obj->GetTxCount();

    if (str[len - 1] == '\n') {
        const char carriage_return = '\r';
        if (serial_obj->Send(&carriage_return, 1) != ARM_DRIVER_OK) {
            return 0;
        }

        while (serial_obj->GetStatus().tx_busy)
            ;
    }

    return tx_count;
}

int iotsdk_serial_in(char *str, size_t len)
{
    if (serial_obj->Receive(str, len) != ARM_DRIVER_OK) {
        return 0;
    }

    /* Receive is non-blocking. Add busy wait to make sure it has completed. */
    while (serial_obj->GetRxCount() < len)
        ;

    return len;
}
